#include "HeaderFileAnalyzer.h"

#include "boost/scoped_ptr.hpp"
#include "boost/format.hpp"
#include <sstream>

#include "Common.h"

using namespace analyzer;

HeaderFileAnalyzer::HeaderFileAnalyzer(void)
	: dependencyGraph(NULL)
{
	// sets up default file system scanner which uses header file filter
	boost::shared_ptr<AbstractFileFilter> filter(new ORFilter(boost::shared_ptr<AbstractFileFilter>(new HeaderFileFilter()), boost::shared_ptr<AbstractFileFilter>(new SourceFileFilter())));
	boost::shared_ptr<AbstractFileSystemScanner> fileSystemScanner(new FileSystemScanner(filter));
	setFileSystemScanner(fileSystemScanner);
	
	boost::shared_ptr<AbstractCycleFinderAlgorithm> cycleFinderAlgorithm(new RecursiveDFSMultipleCycleFinderAlgorithm());
//	boost::shared_ptr<AbstractCycleFinderAlgorithm> cycleFinderAlgorithm(new IterativeDFSMultipleCycleFinderAlgorithm());
	setCycleFinderAlgorithm(cycleFinderAlgorithm);

	includeSubDirectories = false;
}

HeaderFileAnalyzer::HeaderFileAnalyzer(boost::shared_ptr<AbstractFileSystemScanner> fileSystemScanner, boost::shared_ptr<AbstractCycleFinderAlgorithm> cycleFinderAlgorithm)
	: dependencyGraph(NULL)
{	
	setFileSystemScanner(fileSystemScanner);
	setCycleFinderAlgorithm(cycleFinderAlgorithm);

	includeSubDirectories = false;
	if (dependencyGraph)
		delete dependencyGraph;
}

HeaderFileAnalyzer::~HeaderFileAnalyzer(void)
{
	// empty descructor
	if (dependencyGraph)
		delete dependencyGraph;
}

boost::shared_ptr<AbstractCycleFinderAlgorithm> HeaderFileAnalyzer::getCycleFinderAlgorithm(void) const {
	return cycleFinderAlgorithm;
}

void HeaderFileAnalyzer::setCycleFinderAlgorithm(boost::shared_ptr<AbstractCycleFinderAlgorithm> cycleFinderAlgorithm) {
	this -> cycleFinderAlgorithm = cycleFinderAlgorithm;
}

boost::shared_ptr<AbstractFileSystemScanner> HeaderFileAnalyzer::getFileSystemScanner(void) const {
	return fileSystemScanner;
}

void HeaderFileAnalyzer::setFileSystemScanner(boost::shared_ptr<AbstractFileSystemScanner> fileSystemScanner) {
	this -> fileSystemScanner = fileSystemScanner;
}

bool HeaderFileAnalyzer::getIncludeSubDirectories(void) const {
	return includeSubDirectories;
}

void HeaderFileAnalyzer::setIncludeSubDirectories(bool includeSubDirectories) {
	this -> includeSubDirectories = includeSubDirectories;
}

void HeaderFileAnalyzer::scanDirectory(std::string directory) {

	log(DEBUG, "HeaderFileAnalyzer", "scanDirectory", "Method entered.");

	boost::scoped_ptr<AbstractSourceFileReader> reader(new SourceFileReader());
	std::vector<std::vector<int> > cyclicVertices;

	log(INFO, boost::format("Scaning files in directory: %1%") % directory);

	// retrieves all the files
	std::vector<FileInfo> files = fileSystemScanner->filterFiles(directory, includeSubDirectories);
	
	if(files.size() != 0) {
		GraphBuilder graphBuilder;
		graphBuilder.createNewGraph();
		
		// adds all found files
		for(std::vector<FileInfo>::iterator it(files.begin()); it != files.end(); ++it) {
			graphBuilder.addVertex(it->getFileName(), it->getFileType());
		}
		// iterates over all found files and reads their imports
		for(std::vector<FileInfo>::iterator it(files.begin()); it != files.end(); ++it) {
			addEdges(graphBuilder, it->getFileName(), reader->readFileImports(*it));
		}
		if (dependencyGraph) {
			delete dependencyGraph;
		}
		dependencyGraph = graphBuilder.getGraph();
		// TODO print out summary of scanned files etc.
		cyclicVertices = cycleFinderAlgorithm -> findCycle(*dependencyGraph);
		
		if(cyclicVertices.size() == 0) {
			log(INFO, "No cyclic dependencies found.");
		} else {
			std::ostringstream msg;

			boost::property_map<Graph, boost::vertex_name_t>::type vertexNames = boost::get(boost::vertex_name_t(), *dependencyGraph);

			// this could be simplified by coping the iterator directly
			for(std::vector<std::vector<int> >::iterator iter(cyclicVertices.begin()); iter != cyclicVertices.end(); ++iter) {

				if(iter != cyclicVertices.begin()) {
					msg << ", ";
				}

				msg << "(";

				std::ostringstream msgPart;
				for(std::vector<int>::iterator it(iter -> begin()); it != iter -> end(); ++it) {
					if(it != iter -> begin()) {
						msgPart << " <- ";
					}

					msgPart << boost::get(vertexNames, *it);
				}

				msg << msgPart.str() << ")";
			}

			log(ERROR, boost::format("Header files contains cyclic dependencies: %1%.") % msg.str());
		}
	} else {

		log(INFO, "No source files found.");
	}


	log(DEBUG, "HeaderFileAnalyzer", "scanDirectory", "Method exited.");
}

void HeaderFileAnalyzer::addEdges(GraphBuilder& graphBuilder, std::string vertexName, const std::vector<std::string>& dependencies) {

	Vertex rootVertex;
	Vertex vertex;
	
	// retrieve the vertex index
	rootVertex = graphBuilder.findEdge(vertexName);
	
	for(std::vector<std::string>::const_iterator it(dependencies.begin());  it != dependencies.end(); ++it) {
		std::pair<Vertex,bool> ret = graphBuilder.addVertex(*it, HEADER_FILE);
		if (ret.second == true) {
			log(ERROR, boost::format("Included file not found: %1%") % *it);
		}
		graphBuilder.addEdge(rootVertex, ret.first);
	}
}
